package cn.zmlio.mybatisdbchecker.genernator;

import com.github.javafaker.Faker;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.*;

public class MockDataGenerator {

    private static final Faker faker = new Faker();
    private static final int MAX_DEPTH = 5;
    private static final Map<String, Object> fixedValues = new HashMap<>();

    static {
        fixedValues.put("tableNo", null);
        fixedValues.put("tableId", null);
        fixedValues.put("tenantId", "1");
    }

    public static <T> T generateMockData(Class<T> clazz) {
        return generateMockData(clazz, 0);
    }

    private static <T> T generateMockData(Class<T> type, int depth) {
        if (depth > MAX_DEPTH) {
            return null;
        }

        try {
            // Handle primitive types and common objects directly
            if (type.isPrimitive() || type == String.class || type == Integer.class || type == Long.class || type == Double.class || type == Boolean.class || type == Date.class || type == BigDecimal.class) {
                Object value = generateValue(type, depth);
                return type.cast(value);
            }

            T instance = type.getDeclaredConstructor().newInstance();
            fillFields(instance, type, depth);
            return instance;
        } catch (Exception e) {
            return null;
        }
    }

    private static void fillFields(Object instance, Class<?> clazz, int depth) {
        if (depth > MAX_DEPTH || clazz == null || clazz == Object.class) {
            return;
        }

        // Handle superclass fields first
        fillFields(instance, clazz.getSuperclass(), depth + 1);

        for (Field field : clazz.getDeclaredFields()) {
            if (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers())) {
                continue;
            }

            try {
                field.setAccessible(true);
                Object value;
                if (fixedValues.containsKey(field.getName())) {
                    value = fixedValues.get(field.getName());
                } else {
                    value = generateValue(field, depth);
                }
                if (value != null) {
                    field.set(instance, value);
                }
            } catch (Exception ignored) {
                // Skip fields that cannot be set
            }
        }
    }

    private static Object generateValue(Field field, int depth) {
        if (depth > MAX_DEPTH) {
            return null;
        }

        Class<?> type = field.getType();

        // Handle collections first
        if (type == List.class) {
            return generateList(field, depth);
        } else if (type == Map.class) {
            return generateMap(field, depth);
        }

        return generateValue(type, depth);
    }

    private static Object generateValue(Class<?> type, int depth) {
        if (type == String.class) {
            return faker.name().username();
        } else if (type == int.class || type == Integer.class) {
            return faker.number().numberBetween(1, 100);
        } else if (type == long.class || type == Long.class) {
            return faker.number().numberBetween(1L, 100L);
        } else if (type == double.class || type == Double.class) {
            return faker.number().randomDouble(2, 1, 100);
        } else if (type == boolean.class || type == Boolean.class) {
            return faker.bool().bool();
        } else if (type == Date.class) {
            return faker.date().birthday();
        } else if (type == BigDecimal.class) {
            return BigDecimal.valueOf(faker.number().randomDouble(2, 1, 100));
        } else {
            return generateMockData(type, depth + 1);
        }
    }

    private static List<Object> generateList(Field field, int depth) {
        if (depth > MAX_DEPTH) {
            return Collections.emptyList();
        }
        List<Object> list = new ArrayList<>();
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genericType;
            Type[] actualTypeArguments = pt.getActualTypeArguments();
            if (actualTypeArguments.length == 1) {
                Class<?> listType = (Class<?>) actualTypeArguments[0];
                for (int i = 0; i < 5; i++) {
                    list.add(generateValue(listType, depth + 1));
                }
            }
        }
        return list;
    }

    private static Map<Object, Object> generateMap(Field field, int depth) {
        Map<Object, Object> map = new HashMap<>();
        Type genericType = field.getGenericType();
        if (genericType instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) genericType;
            Type[] actualTypeArguments = pt.getActualTypeArguments();
            if (actualTypeArguments.length == 2) {
                Class<?> keyType = (Class<?>) actualTypeArguments[0];
                Class<?> valueType = (Class<?>) actualTypeArguments[1];
                for (int i = 0; i < 5; i++) {
                    map.put(generateValue(keyType, depth + 1), generateValue(valueType, depth + 1));
                }
            }
        }
        return map;
    }
}
